package com.sinaapp.gavinzhang.GSpider.process;

import com.sinaapp.gavinzhang.GSpider.config.Constants;
import com.sinaapp.gavinzhang.GSpider.config.PageProcessorConfig;
import com.sinaapp.gavinzhang.GSpider.core.AbstractPageProcessor;
import com.sinaapp.gavinzhang.GSpider.core.AbstractProcessor;
import com.sinaapp.gavinzhang.GSpider.core.PageProcessor;
import com.sinaapp.gavinzhang.GSpider.webaddress.URLDispatcher;
import com.sinaapp.gavinzhang.GSpider.webaddress.WebAddress;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * @author gavin
 * @version 1.0.0
 * @description AbstractProcess类的实现
 * 流程：
 * websiteCheckAndDispose
 * 当 (网址为空 || 已经爬过 || http状态码有误)将网址置空返回。
 * 将网址放入已爬列表。
 * 得到网页，如果出错则将html和wenurl置空。
 * <p/>
 * 以下函数都将判断html是否为空
 * <p/>
 * preWebContent
 * 将得到的页面经由用户处理，返回处理国的Elements。
 * <p/>
 * disposeWebContent
 * 抓取符合规则的网址，放入临时网址列表
 * <p/>
 * afterWebContent
 * 将原始网页交由用户处理。
 * 将用额外加入的网址放入临时网址列表。
 * 将临时列表的网址置入待爬列表。
 * @date 2015/9/10
 * @see AbstractProcessor
 */
public class Process extends AbstractProcessor {

    private static Logger logger = LoggerFactory.getLogger(Process.class);

    private WebAddress webUrl;
    private Document html;
    private Elements elements;
    private boolean starting;

    private List<Pattern> patterns;
    private PageProcessor pp;
    private HttpProcessorInterceptor httpProcessorInterceptor;

    private URLDispatcher urlDispatcher;

    private PageProcessorConfig pageProcessorConfig;


    protected List<WebAddress> urllist = new ArrayList<>();

    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            if (pageProcessorConfig.getPatterns().size() != 0) {
                try {
                    this.webUrl = urlDispatcher.get();
                    /** 判断网址是否为空，是否抓过，网站状态检查，下载*/
                    websiteCheckAndDispose(this.webUrl);
                    /** 预处理页面，从用户处得到处理后的elements，并且只从elements中爬取规则*/
                    preWebContent(this.html);
                    /** 从页面中得到可用的网址，存入list*/
                    disposeWebContent(this.elements);
                    /** 将原始网页交由用户处理。添加从用户得到网址*/
                    afterWebContent(this.html);
                } catch (Exception e) {
                    if (e.getMessage() != null && e.getMessage().contains("connect")) {
                        pageProcessorConfig.getPatterns().clear();
                    }
                }
            } else {
                System.out.print("等待传入配置文件\r");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void websiteCheckAndDispose(WebAddress webUrl) {
        if (null == webUrl) return;

        HttpProcessor httpProcessor = new HttpProcessor(webUrl.getUrl());
        int status = httpProcessor.getStatus();
        /**判断http返回状态码，大于400 直接返回 bug：429为请求太多 503服务器主动拒绝*/
        switch (status) {
            case 200:
                break;
            case 403:
            case 404:
            case 500:
                logger.error(Constants.SERVICE_ERROR + status + this.webUrl);
                this.webUrl = null;
                this.html = null;
                return;
            default:
                break;
        }

        /** 最多timeout次判断 当status为429或200时
         *  去掉(status==429 || status==503 || status==200) &&
         * */
        int timeout = pageProcessorConfig.getTimeout();
        int sleepTime = pageProcessorConfig.getSleepTime();

        while (--timeout > 0) {
            try {
                //TODO use list in future
                httpProcessorInterceptor.preProcess(httpProcessor);
                html = Jsoup.parse(httpProcessor.getContent(), webUrl.getUrl());
                httpProcessorInterceptor.afterProcess(httpProcessor);

                timeout = 0;
            } catch (IOException e) {
                //协议 网址 连接出错
                //e.printStackTrace();
            }
            /** 如果不等于0 则说明没有拿到网页数据*/
            if (timeout != 0) {
                logger.warn("超时重试次数：" + (pageProcessorConfig.getTimeout() - timeout) + " " + this.webUrl);
                status = httpProcessor.getStatus();
                if (status == 429 || status == 503) {
                    try {
                        Thread.sleep(sleepTime);
                        sleepTime = sleepTime + pageProcessorConfig.getWaitTime();
                    } catch (InterruptedException e) {
                        //中断结束
                        break;
                    }
                }
            }
        }
        if (html == null) {
            logger.error(Constants.CATCH_URL_ERROR + this.webUrl);
        }
    }


    @Override
    public void preWebContent(Document html) {
        if (null == html) return;
        /** 从用户处得到要处理的页面*/
        Elements tmp = pp.preDispose(html);
        if (tmp != null) {
            elements = tmp;
        } else {
            elements = html.select("body");
        }
    }

    @Override
    public void disposeWebContent(Elements html) {
        /** 如果html为空则直接返回*/
        if (null == html) return;
        Elements elements = this.elements.select("a");
        /** 如果正则表达式列表没有设置则有问题.由GSpider.check方法检查*/
        if (patterns == null || patterns.size() == 0 || elements == null) return;
        /** 的到符合规则的网址，存入临时列表*/
        for (Pattern pa : patterns) {
            for (Element e : elements) {
                if (e.absUrl("href").matches(pa.toString())) {
                    urllist.add(new WebAddress(e.absUrl("href"), this.webUrl.getDeep() + 1, this.webUrl.getImportance() - 1));
                }
            }
        }
    }

    @Override
    public void afterWebContent(Document html) {
        /** 在chenkanddown 后都用这个来判断是否有问题*/
        if (null == html) return;
        /** 先添加找到的网址，后再加用户找到的*/

        logger.info("find " + urllist.size() + " urls");
        for (WebAddress webAddress : urllist) {
            urlDispatcher.add(webAddress);
        }

        if (urllist.size() != 0) {
            urllist = new ArrayList<>();
        }

        //urllist.clear();
        /** 用户处理页面*/
        pp.dispose(html, webUrl);
        /** 得到额外的网址列表,暂时不能使用*/
        List<WebAddress> tmpList = pp.getUrlList();
        if ((tmpList != null) && (tmpList.size() != 0)) {
            for (WebAddress webAddress : tmpList) {
                urlDispatcher.add(webAddress);
            }
        }


        webUrl = null;
        this.html = null;
    }

    public void setPageProcessorConfig(PageProcessorConfig pageProcessorConfig) {
        this.pageProcessorConfig = pageProcessorConfig;
        this.pp = pageProcessorConfig.getPageProcessor();
        this.patterns = pageProcessorConfig.getPatterns();
        this.httpProcessorInterceptor = pageProcessorConfig.getHttpProcessorInterceptor();
        this.urlDispatcher = pageProcessorConfig.getUrlDispatcher();
    }


    public Elements getElements() {
        return elements;
    }

    public void setElements(Elements elements) {
        this.elements = elements;
    }

    @Override
    public void setPatterns(List<Pattern> patterns) {
        this.patterns = patterns;
    }

    @Override
    public void setPageProcessor(AbstractPageProcessor pp) {
        this.pp = pp;
    }

    @Override
    public void setURLDispatcher(URLDispatcher urlDispatcher) {
        this.urlDispatcher = urlDispatcher;
    }

    public boolean isStarting() {
        return starting;
    }

    public void setStarting(boolean starting) {
        this.starting = starting;
    }

    public void setHttpProcessorInterceptor(HttpProcessorInterceptor httpProcessorInterceptor) {
        this.httpProcessorInterceptor = httpProcessorInterceptor;
    }
}
